ciftiTools DemociftiTools is an R package for working with CIFTI-2 format brain imaging data. Used in conjunction with GIFTI surface geometry files, CIFTI files enable surface-based analysis of gray matter data, which has several advantages over traditional volumetric/voxel-based analysis. Because of this, the CIFTI-2 format is used by recent neuroimaging studies including the Human Connectome Project (HCP). ciftiTools supports reading, writing, visualizing, resampling, and other operations for CIFTI files with the ".dscalar.nii", ".dtseries.nii", and ".dlabel.nii" intents. Several of these operations are made possible by the Connectome Workbench.
To get started, the first time you use ciftiTools, install it from either CRAN with install.packages() or Github with devtools::install_github(). Here, we will use the CRAN version.
# Check if package installed. If not, install it.
if(!require('ciftiTools', quietly=TRUE)){
install.packages('ciftiTools')
# devtools::install_github('mandymejia/ciftiTools') # development version
}Now we load the ciftiTools package.
library(ciftiTools)Next, we indicate where to find the Connectome Workbench. This can be the full path to the Connectome Workbench executable file, or the path to its containing folder, in which case ciftiTools will locate the full path. Here, we will use the latter:
# Replace '../../workbench' with the actual path to
# the Connectome Workbench folder on your computer.
# If successful, the path to the Workbench executable will be printed.
ciftiTools.setOption('wb_path', '../../workbench')## Using this Workbench path: '../../workbench/bin_windows64/wb_command.exe'.
In this vignette, we will use example data included in the ciftiTools package. The files are originally from NITRC:
ciftiTools, the dscalar and dlabel CIFTIs were resampled to 6k and the “ones” dscalar was resampled to 1k.We will also use GIFTI files containing inflated surface geometry.
cifti_fnames <- ciftiTools.files()$cifti
surfL_fname <- ciftiTools.files()$surf["left"]
surfR_fname <- ciftiTools.files()$surf["right"]CIFTI files organize the gray matter of the brain into “grayordinates”: vertices representing the left and right cortical surfaces, and voxels representing the subcortical gray matter structures and the cerebellum. A CIFTI file consists of two parts: (1) an XML header which contains all the metadata including medial wall locations, subcortical structure labels, and the subcortical volumetric mask; and (2) a NIFTI format matrix representing all the grayordinate data. These components are read in together with read_cifti:
basename(cifti_fnames["dtseries"])## [1] "Conte69.MyelinAndCorrThickness.32k_fs_LR.dtseries.nii"
xii <- read_xifti(cifti_fnames["dtseries"])The result of read_cifti is a "xifti" object. We can get an overview of it using its summary S3 method:
xii # same as `summary(xii)`## ====CIFTI METADATA===================
## Intent: 3002 (dtseries)
## - time step 1 (seconds)
## - time start 0
## Measurements: 2 columns
##
## ====BRAIN STRUCTURES=================
## - left cortex 30424 data vertices
## 2068 medial wall vertices (32492 total)
##
## - right cortex 30527 data vertices
## 1965 medial wall vertices (32492 total)
By default, read_cifti only reads in the left and right cortex data. The subcortical data can be included by using the argument brainstructures="all". Other brainstructure combinations can be specified too, e.g. brainstructures=c("left", "subcortical"). The full set of choices for brainstructures is any combination of "left", "right" and "subcortical", or "all" for all three.
"xifti" objects are lists with entries data (the grayordinate data matrix, separated by brainstructure), surf (surface geometry), and meta (metadata, most of which is from the NIFTI XML header). surf distinguishes a "xifti" from a CIFTI file: the left and right cortical surface geometries are not included in CIFTI files, so they must be read from separate surface GIFTI files (ending in surf.gii). The surface must be compatible: the number of vertices must be the same, and each vertex in the CIFTI data must correspond to the vertex location in the corresponding GIFTI surface file. In this way, a "xifti" represents a combination of a CIFTI file with compatible GIFTI files for the cortical mesh.
We can add GIFTI surface geometries with add_surf:
xii <- add_surf(xii, surfL=surfL_fname, surfR=surfR_fname)
xii## ====CIFTI METADATA===================
## Intent: 3002 (dtseries)
## - time step 1 (seconds)
## - time start 0
## Measurements: 2 columns
##
## ====BRAIN STRUCTURES=================
## - left cortex 30424 data vertices
## 2068 medial wall vertices (32492 total)
## left surface geometry is present
##
## - right cortex 30527 data vertices
## 1965 medial wall vertices (32492 total)
## right surface geometry is present
Alternatively, we could have provided the surface geometries at the outset of reading the CIFTI file:
xii2 <- read_xifti(cifti_fnames["dtseries"], surfL_fname=surfL_fname, surfR_fname=surfR_fname)
all.equal(xii, xii2) # same result## [1] TRUE
To only read the CIFTI header, use info_cifti. Let’s read the header of the dscalar file:
xii_info <- ciftiTools::info_cifti(cifti_fnames["dscalar"])
str(xii_info, nchar.max=50) # shows header structure## List of 3
## $ cortex :List of 2
## ..$ medial_wall_mask:List of 2
## .. ..$ left : logi [1:5762] TRUE TRUE TRUE TRUE TRUE TRUE ...
## .. ..$ right: logi [1:5762] TRUE TRUE TRUE TRUE TRUE TRUE ...
## ..$ resamp_res : NULL
## $ subcort:List of 3
## ..$ labels : NULL
## ..$ mask : NULL
## ..$ trans_mat: NULL
## $ cifti :List of 4
## ..$ intent : num 3006
## ..$ brainstructures: chr [1:2] "left" "right"
## ..$ names : chr [1:2] "MyelinMap_BC_decurv" "corrThickness"
## ..$ misc :List of 4
## .. ..$ ParentProvenance : chr "C:\\Users\\damon\\AppData\\Local\"| __truncated__
## .. ..$ ProgramProvenance: chr "Connectome Workbench\nType: Comma"| __truncated__
## .. ..$ Provenance : chr "C:\\Users\\damon\\Desktop\\WORKBE"| __truncated__
## .. ..$ WorkingDirectory : chr "C:/Users/damon/Desktop/ciftiTools/vignettes"
To read in only certain columns of a CIFTI file, use the idx argument:
read_xifti(cifti_fnames["dtseries"], idx=2) # second column only## ====CIFTI METADATA===================
## Intent: 3002 (dtseries)
## - time step 1 (seconds)
## - time start 0
## Measurements: 1 column
##
## ====BRAIN STRUCTURES=================
## - left cortex 30424 data vertices
## 2068 medial wall vertices (32492 total)
##
## - right cortex 30527 data vertices
## 1965 medial wall vertices (32492 total)
When a "xifti" object is written to files, the CIFTI components are placed in a CIFTI file and the surface geometries, if any, are placed in GIFTI files.
out_dir <- "output"
write_xifti(
xii,
file.path(out_dir, "my_cifti.dtseries.nii"),
file.path(out_dir, "my_L.surf.gii"), file.path(out_dir, "my_R.surf.gii")
)## Writing left cortex.
## Writing right cortex.
## Creating CIFTI file from separated components.
## Writing surface geometry GIFTI(s).
With separate_cifti, a CIFTI can be separated and written into its component parts: the cortical data can be written to GIFTI metric or label files, and the subcortical data can be written to a NIFTI file. In addition, any ROIs or labels will also be written to files. The files are automatically named unless a new file name is provided.
# Use default names for everything except left cortex
separated_fnames = separate_cifti(
cifti_fnames["dscalar_ones"], brainstructures="all",
cortexL_fname="my_left_cortex.func.gii", write_dir = out_dir
)
# Files written to `out_dir`, or current working dir. if not specified
basename(separated_fnames)## [1] "my_left_cortex.func.gii" "ones_1k.ROI_L.func.gii"
## [3] "ones_1k.R.func.gii" "ones_1k.ROI_R.func.gii"
## [5] "ones_1k.nii" "ones_1k.labels.nii"
## [7] "ones_1k.ROI.nii"
Separated files can be read into R with the oro.nifti, RNifti, and gifti packages, and combined into a "xifti" object with as.xifti.
The cortical plots in ciftiTools are made possible by the rgl package. To prepare the R Markdown document for knitting we need to do the following:
library(rgl)## Warning: package 'rgl' was built under R version 4.1.1
rgl::setupKnitr()
# Sometimes the first OpenGL window does not render properly.
rgl::rgl.open(); rgl::rgl.close()Now let’s take a look!
view_xifti_surface(xii) displays the cortical data on the surface mesh. This function has several primary arguments:
color_mode specifies the nature of the data values: "sequential", "qualitative" and "diverging". If it is not provided, a default mode that makes sense for the data will be used.colors specifies the color palette to use. If it is not provided, a default palette that makes sense for the color_mode is used.idx controls which column(s) to display.widget and fname control the output type. If fname is not provided, an interactive plot is created: by default, an OpenGL window if the length of idx is one, and an embedded HTML widget if the length of idx is greater than one. widget can be used to override this default. On the other hand, if fname is provided, static image files (png) for each idx are created, unless fname ends in .html in which case an interactive html file will be saved. Lastly, both OpenGL windows and HTML widgets can be embedded in R Markdown documents for knitting; refer to the source code of this vignette to see how this works.surfL and surfR specify the surface geometry to plot the data on. If not provided, the surfaces in the "xifti" object is used. But if those are also unavailable, the “inflated” surfaces included in ciftiTools are used.Let’s see an example using each color_mode option. Note how the included surfaces are used in the first plot, but if none are present as in the second and third plots, the default surfaces are automatically used for visualization. (In our case, the included surfaces are the default surfaces.) We’ll also make the second plot interactive by requesting display of two idx. Try clicking and dragging around the second plot to rotate, and scrolling to zoom in and out. Note that the first and third plots are OpenGL window snapshots, and that the second plot is an embedded HTML widget.
# Normally `cex.title` doesn't need to be set, as it defaults to a good choice.
# But when knitting static images this way, the default becomes a bit too big
# based on how knitting works.
view_xifti_surface(xii, idx=1, zlim=c(1,2), title='color_mode = "sequential"', cex.title=1.3)dtseries file; first column; sequential palette
xii <- read_cifti(cifti_fnames["dscalar"]) # no GIFTI included, so the default inflated surface is used.
view_xifti_surface(
xii, idx=1:2, zlim=c(0,5), color_mode = "diverging",
title='color_mode = "diverging"', cex.title=1.3
)